
#pragma once

#include "mymath.h"
#include "cmnBlocks.h"

#include <cstdlib>

namespace EggNine {

class ICamera {
public:
    virtual void UpdateCameraMatrix()=0;
    
    virtual void TranslateTo(float x, float y, float z)=0;
    virtual void TranslateBy(float dx, float dy, float dz)=0;

    virtual void PointTo(const vec4& loc, const vec4& pt, const vec4& up)=0;
    virtual void RotateCameraBy(float dphi, float dtheta, float dpsi)=0;
    virtual void RotateAroundAxis(float x, float y, float z, float angle)=0;

    virtual void ZoomBy(float magnitude)=0;

    virtual void Reshape(int widht, int height)=0;

    virtual ~ICamera() {}

    void* operator new(size_t size)
    {
        return _aligned_malloc(size, 16);
    }
    void operator delete(void* addr)
    {
        _aligned_free(addr);
    }
};


class PerspectiveCamera : public ICamera {
    // The logical order of application is: Rotate_psi,theta(Translate(Rotate_phi(Orient(camera))))
    // Actual is in reverse: Orient o Translate o Rotate_phi o Rotate_theta o Rotate_psi o camera

    // Order of application to the camera is: R_psi(R_theta(R_phi(T(x))))
    vec4            m_qBaseOrientation; // Base orientation of camera (before translation)
    vec4            m_qRotPhi;          // z rotation (left,right)
    vec4            m_qRotTheta;        // x rotation (up,down)
    vec4            m_qRotPsi;          // y rotation (sideways)
    vec4            m_Trans;            // Location of camera relative to orientation

    CameraBlock::SMatrices  m_matrices;
    CameraBlock::SMatrices  m_inverseMatrices;

    float           m_near, m_far, m_fov, m_ar;     // Projection stuff
    float           m_tripodHeight;                 // For future use
    bool            m_lockToY;                      // Are we on a tripod?

protected:
    void UpdateProjection();

public:
    PerspectiveCamera(float nearPlane, float farPlane, float fov, float aspectRatio, float tripodHeight=0.f);

    void RotateCameraBy(float dphi, float dtheta, float dpsi);
    void TranslateBy(float dx, float dy, float dz); // Relative to orientation
    void TranslateTo(float x, float y, float z);    // Absolute in world
    void ZoomBy(float magnitude);
    void RotateAroundAxis(float x, float y, float z, float angle);
    void PointTo(const vec4& loc, const vec4& pt, const vec4& up);

    void Reshape(int width, int height);

    void UpdateCameraMatrix();
};

} // EggNine